package com.zfei.gfb.controller;


import cn.hutool.core.lang.Validator;
import cn.hutool.http.HttpUtil;
import com.google.gson.JsonObject;
import com.zfei.gfb.GfbService;
import com.zfei.gfb.bean.Const;
import com.zfei.gfb.bean.DataMap;
import com.zfei.gfb.bean.Status;
import com.zfei.gfb.bean.StatusException;
import com.zfei.gfb.core.Auth;
import com.zfei.gfb.core.Conf;
import com.zfei.gfb.core.HttpJson;
import com.zfei.gfb.core.LogResult;
import com.zfei.gfb.dao.GfbCompanyMapper;
import com.zfei.gfb.dao.GfbGuestMapper;
import com.zfei.gfb.dao.GfbVillagerMapper;
import com.zfei.gfb.device.AppContext;
import com.zfei.gfb.model.GfbCompany;
import com.zfei.gfb.model.GfbGuest;
import com.zfei.gfb.model.GfbVillager;
import com.zfei.gfb.model.GfbWorkpoint;
import com.zfei.gfb.request.h5.AuthCompanyRequest;
import com.zfei.gfb.request.h5.AuthVillagerRequest;
import com.zfei.gfb.request.h5.OpenIdRequest;
import com.zfei.gfb.request.h5.SmsCodeRequest;
import com.zfei.gfb.util.Gsons;
import com.zfei.gfb.util.NewsUtil;
import com.zfei.gfb.util.Now;
import com.zfei.gfb.util.SmsUtil;
import com.zfei.gfb.vo.LoginVo;
import com.zfei.gfb.vo.WorkpointVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author fgh
 */
@Api(tags = "移动端登录接口")
@RestController
@RequestMapping("/h5")
public class H5LoginController {

    String PREX_WX_TOKEN = "WX_TOKEN";
    String PREX_WX_TICKET = "WX_TICKET";

    public static final String appId = "wxdb4fad72b6e30136";
    public static final String appSecret = "4b3aad2910db6ca8d601106d7c76c4b9";

    private static final Logger logger = LoggerFactory.getLogger(H5LoginController.class);

    @Resource
    private GfbGuestMapper gfbGuestMapper;

    @Resource
    private GfbVillagerMapper gfbVillagerMapper;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private HttpSession httpSession;

    @Resource
    private GfbCompanyMapper gfbCompanyMapper;

    @Resource
    private GfbService gfbService;


    @Resource
    private JdbcTemplate jdbcTemplate;


    @LogResult
    @ApiOperation(value = "获取短信验证码")
    @HttpJson("/sms/code")
    public Object smsCode(@RequestBody SmsCodeRequest request) {

        String phone = request.getPhone();
        if (phone == null) {
            throw new StatusException(Status.INVALID_ARGUMENT);
        }

        if (!Validator.isMobile(phone)) {
            throw new StatusException(Status.INVALID_ARGUMENT, "phone format error");
        }

        //验证上次操作是否超过一分钟，查找key=mobile记录，如存在，返回错误码操作太频繁
        RBucket frequency_check = redissonClient.getBucket(Const.PREX_SMSCODE_FREQUENCY_CHECK + phone);
        if (frequency_check.isExists()) {
            throw new StatusException(Status.FREQUENTLY);
        }

        //开关，测试时不要浪费短信
        boolean sendSms = Conf.getBoolean("sms.send.enable", true);
        //存入redis，有效期3分钟
        int code;
        RBucket<Integer> rBucket_store = redissonClient.getBucket(Const.PREX_SMSCODE_STORE + phone);
        if (rBucket_store.get() != null) {
            code = rBucket_store.get();
        } else {
            code = (int) ((Math.random() * 9 + 1) * 1000);
            if (!sendSms) {
                code = 9999;
            }
            rBucket_store.set(code, 30L, TimeUnit.MINUTES);
        }
        //redis还需要存入code，有效期1分钟
        frequency_check.set(code, 1L, TimeUnit.MINUTES);

        if (sendSms) {
            SmsUtil.send(phone, String.valueOf(code));
        }
        return null;
    }

    @ApiOperation(value = "微信跳转自动登录", notes = "工分宝村民账号跳转时，如携带phone、name、idCard参数，自动进行村民身份认证并登录，测试uuid=mADLP6GtcMTZDZkIdqYPsi9SYBsc-EX2")
    @ApiResponses({
            @ApiResponse(code = 200, message = "返回用户信息", response = LoginVo.class),
    })
    @LogResult
    @HttpJson("/login/auto")
    public Object login(@RequestBody OpenIdRequest request) {

        request.checkParam();
        httpSession.invalidate();
        String openId = request.getOpenId();

        LoginVo loginVo = new LoginVo();

        GfbGuest guest = gfbGuestMapper.selectByOpenId(openId);
        if (guest == null) {
            //创建游客
            guest = gfbService.addGuest(openId);
        }

        if (Const.isGuest(guest.getLastLoginType())) {
            //工分宝跳转，自动认证村民
            if (request.autoValidateVillager()) {
                boolean auth = true;
                GfbVillager realVillager = null;

                try {
                    realVillager = gfbService.confirm(request.getUuid());
                } catch (StatusException ignore) {
                    loginVo.setMsg("uuid认证，村民不存在");
                    auth = false;
                }
                if (auth) {
                    GfbVillager dbVillager = gfbVillagerMapper.selectByPhone(realVillager.getPhone());
                    if (dbVillager == null) {
                        dbVillager = gfbService.addVillager(request.getUuid(), realVillager.getPhone(), realVillager.getName(), realVillager.getIdCard(), realVillager.getSourceUserId(), realVillager.getVillagerName(),
                                realVillager.getTown(), realVillager.getNaturalVillageId(), realVillager.getNaturalVillageName(), realVillager.getGroupNumber(), realVillager.getAddress());
                    }else {
                        realVillager.setId(dbVillager.getId());
                        gfbService.updateVillager(realVillager);
                    }
                    //认证游客为村民
                    guest.setLastLoginType(Const.VILLAGER);
                    guest.setLastLoginId(dbVillager.getId());
                    gfbGuestMapper.updateByPrimaryKeySelective(guest);

                    httpSession.setAttribute(AppContext.SESSION_H5_VILLAGER, dbVillager);
                    loginVo.setType(Const.VILLAGER);
                    loginVo.setVillager(dbVillager);
                    return loginVo;
                }
            }

            httpSession.setAttribute(AppContext.SESSION_H5_GUEST, guest);
            loginVo.setType(Const.GUEST);
            loginVo.setGuest(guest);

            return loginVo;
        }


        if (Const.isVillager(guest.getLastLoginType())) {
            loginVo.setType(Const.VILLAGER);
            Integer lastLoginId = guest.getLastLoginId();
            if (lastLoginId == null) {
                throw new StatusException(Status.UNAUTHORIZED, "lastType和lastId必须同时存在");
            }
            GfbVillager villager = gfbVillagerMapper.selectByPrimaryKey(lastLoginId);
            if (villager == null) {
                throw new StatusException(Status.UNAUTHORIZED, "lastId对应的村民不存在");
            }
            if (request.autoValidateVillager() && !request.getUuid().equals(villager.getExt())) {
                //同一个openId，换了村民绑定，需要更新
                GfbVillager realVillager = null;
                boolean auth = true;
                //查询新uuid是否绑定过村民

                //数据库没有uuid的村民信息，需要远程认证
                try {
                    realVillager = gfbService.confirm(request.getUuid());
                } catch (StatusException ignore) {
                    auth = false;
                }
                if (auth) {
                    GfbVillager newVillager = gfbVillagerMapper.selectByPhone(realVillager.getPhone());
                    if (newVillager == null) {
                        newVillager = gfbService.addVillager(request.getUuid(), realVillager.getPhone(), realVillager.getName(), realVillager.getIdCard(), realVillager.getSourceUserId(), realVillager.getVillagerName(),
                                realVillager.getTown(), realVillager.getNaturalVillageId(), realVillager.getNaturalVillageName(), realVillager.getGroupNumber(), realVillager.getAddress());
                    }else {
                        realVillager.setId(newVillager.getId());
                        gfbService.updateVillager(realVillager);
                    }
                    //更新认证新村民，即游客对应最后登录村民为当前uuid
                    guest.setLastLoginType(Const.VILLAGER);
                    guest.setLastLoginId(newVillager.getId());
                    gfbGuestMapper.updateByPrimaryKeySelective(guest);

                    httpSession.setAttribute(AppContext.SESSION_H5_VILLAGER, newVillager);
                    loginVo.setVillager(newVillager);
                }else {
                    //新uuid的村民已存在，直接返回村民信息，更新guest绑定的村民，这时，存在两个guest同时绑定了该村民
                    //认证不成功，仍然绑定上一次的村民
                    httpSession.setAttribute(AppContext.SESSION_H5_VILLAGER, villager);
                    loginVo.setVillager(villager);
                }
                return loginVo;
            }
            httpSession.setAttribute(AppContext.SESSION_H5_VILLAGER, villager);

            loginVo.setVillager(villager);
            return loginVo;
        }
        if (Const.isCompany(guest.getLastLoginType())) {
            Integer lastLoginId = guest.getLastLoginId();
            if (lastLoginId == null) {
                throw new StatusException(Status.UNAUTHORIZED, "lastType和lastId必须同时存在");
            }
            GfbCompany company = gfbCompanyMapper.selectByPrimaryKey(lastLoginId);
            if (company == null) {
                throw new StatusException(Status.UNAUTHORIZED, "lastId对应的企业不存在");
            }
            httpSession.setAttribute(AppContext.SESSION_H5_COMPANY, company);
            loginVo.setType(Const.COMPANY);
            loginVo.setCompany(company);
            return loginVo;
        }
        return null;

    }


    @Auth(type = Const.GUEST)
    @ApiOperation(value = "村民认证", notes = "必须先游客模式登录-login/auto")
    @LogResult
    @HttpJson("/villager/auth")
    public Object authVillager(@RequestBody AuthVillagerRequest authVillagerRequest) {
        //身份证，姓名，手机号，验证码
        authVillagerRequest.checkInsert();


        String phone = authVillagerRequest.getPhone();
        String name = authVillagerRequest.getName();
        String idCard = authVillagerRequest.getIdCard();
        String code = authVillagerRequest.getCode();

        DataMap<String, Object> result = DataMap.build();


        RBucket<Integer> rBucket_store = redissonClient.getBucket(Const.PREX_SMSCODE_STORE + phone);
        if (!rBucket_store.isExists()) {
            throw new StatusException(Status.SMS_CODE_EXPIRED);
        }
        boolean sendSms = Conf.getBoolean("sms.send.enable", true);
        if (sendSms) {
            if (!rBucket_store.get().toString().equals(code)) {
//                rBucket_store.delete();
                throw new StatusException(Status.NOT_MATCH);
            }
        }

        GfbVillager villager = gfbVillagerMapper.selectByPhone(phone);
        //存在村民信息即已通过认证
        if (villager == null) {
            throw new StatusException(Status.NOT_EXIST, "手机号不存在");
        }
        if (!villager.getIdCard().equals(idCard)) {
            throw new StatusException(Status.NOT_MATCH, "身份证错误");
        }
        if (!villager.getName().equals(name)) {
            throw new StatusException(Status.NOT_MATCH, "名字错误");
        }
        //更新游客最近登录信息
        GfbGuest guest = (GfbGuest) httpSession.getAttribute(AppContext.SESSION_H5_GUEST);
        guest.setLastLoginType(Const.VILLAGER);
        guest.setLastLoginId(villager.getId());
        gfbGuestMapper.updateByPrimaryKeySelective(guest);

        httpSession.removeAttribute(AppContext.SESSION_H5_GUEST);
        //存在表示认证过，登录成功，返回用户和村民信息
        httpSession.setAttribute(AppContext.SESSION_H5_VILLAGER, villager);
        result.put("type", Const.VILLAGER);
        result.put("villager", villager);
        return result;
    }


    @Auth(type = Const.GUEST)
    @LogResult
    @ApiOperation(value = "企业账号登录", notes = "必须先游客模式登录-login/auto")
    @HttpJson("/company/login")
    public Object companyLogin(@RequestBody AuthCompanyRequest request) {
        request.checkParam();
        String phone = request.getPhone();
        String code = request.getCode();

        DataMap<String, Object> result = DataMap.build();

        if (StringUtils.isAnyBlank(phone, code)  || code.length() != 4) {
            throw new StatusException(Status.INVALID_ARGUMENT);
        }

        RBucket<Integer> rBucket_store = redissonClient.getBucket(Const.PREX_SMSCODE_STORE + phone);
        if (!rBucket_store.isExists()) {
            throw new StatusException(Status.SMS_CODE_EXPIRED);
        }
        boolean sendSms = Conf.getBoolean("sms.send.enable", true);
        if (sendSms) {
            if (!rBucket_store.get().toString().equals(code)) {
                rBucket_store.delete();
                throw new StatusException(Status.NOT_MATCH, "验证码不匹配");
            }
        }

        GfbCompany company = gfbCompanyMapper.selectByPhone(phone);
        if (company == null) {
            company = gfbService.addCompany(phone);
        }
        //更新游客最近登录信息
        GfbGuest guest = (GfbGuest) httpSession.getAttribute(AppContext.SESSION_H5_GUEST);
        guest.setLastLoginType(Const.COMPANY);
        guest.setLastLoginId(company.getId());
        gfbGuestMapper.updateByPrimaryKeySelective(guest);

        httpSession.removeAttribute(AppContext.SESSION_H5_GUEST);
        //存在表示认证过，登录成功，返回用户和村民信息
        httpSession.setAttribute(AppContext.SESSION_H5_COMPANY, company);
        result.put("type", Const.COMPANY);
        result.put("company", company);
        return result;
    }


    @Auth(type = {Const.VILLAGER, Const.COMPANY})
    @LogResult
    @ApiOperation(value = "登出", notes = "")
    @HttpJson("/loginOut")
    public Object loginOut(@RequestBody OpenIdRequest request) {

        GfbGuest guest = gfbGuestMapper.selectByOpenId(request.getOpenId());
        guest.setLastLoginType(null);
        guest.setLastLoginId(null);
        guest.setUpdateTime(Now.millis());
        gfbGuestMapper.updateByPrimaryKey(guest);

        httpSession.invalidate();
        httpSession.setAttribute(AppContext.SESSION_H5_GUEST, guest);
        return null;
    }

    @LogResult
    @Auth(type = {Const.VILLAGER})
    @ApiOperation(value = "村民工分记录")
    @HttpJson("/workpoint")
    @ApiResponses({
            @ApiResponse(code = 200, message = "村民工分记录", response = GfbWorkpoint.class),
    })
    public Object balance() {
        GfbVillager villager = (GfbVillager) httpSession.getAttribute(AppContext.SESSION_H5_VILLAGER);
        WorkpointVo workpointVo = new WorkpointVo();

        int balance = gfbService.balance(villager.getSourceUserId());
        workpointVo.setBalance(balance);

        String sql = "select * from gfb_workpoint where user_id = " + villager.getId();
        List<GfbWorkpoint> workpointList = jdbcTemplate.query(sql, new Object[]{}, new BeanPropertyRowMapper<>(GfbWorkpoint.class));

        workpointVo.setRecords(workpointList);

        return workpointVo;
    }


    @LogResult
    @HttpJson("/wx/sign")
    public Object sign(@RequestBody JsonObject req) {

        String jsurl = Gsons.getString(req, "url");
        String timestamp = NewsUtil.create_timestamp();
        String nonceStr = NewsUtil.create_nonce_str();
        String token;
        RBucket<String> token_store = redissonClient.getBucket(PREX_WX_TOKEN);
        if (token_store.isExists()) {
            token = token_store.get();
        } else {
            String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appId + "&secret=" + appSecret;
            String token_json = HttpUtil.get(url);
            logger.info("wxresult={}", token_json);
            JsonObject jsonObject = Gsons.parseJsonObject(token_json);
            token = jsonObject.get("access_token").getAsString();
            token_store.set(token, 60L, TimeUnit.MINUTES);
        }

        String ticket;
        RBucket<String> ticket_store = redissonClient.getBucket(PREX_WX_TICKET);
        if (ticket_store.isExists()) {
            ticket = ticket_store.get();
        } else {
            String ticketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token + "&type=jsapi";
            String ticket_json = HttpUtil.get(ticketUrl);
            logger.info("wxresult={}", ticket_json);
            JsonObject ticketJsonObject = Gsons.parseJsonObject(ticket_json);
            ticket = ticketJsonObject.get("ticket").getAsString();
            ticket_store.set(ticket, 60L, TimeUnit.MINUTES);
        }

        // 注意 URL 一定要动态获取，不能 hardcode
        Map<String, String> ret = NewsUtil.sign(nonceStr, timestamp, ticket, jsurl);
        ret.put("appId", appId);
        return ret;

    }

}
